/* 
 -----------------------------------------------------------------------------
                   Cogaen - Component-based Game Engine (v3)
 -----------------------------------------------------------------------------
 This software is developed by the Cogaen Development Team. Please have a 
 look at our project home page for further details: http://www.cogaen.org
    
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Copyright (c) 2010 Roman Divotkey

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in
 all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/

package org.cogaen.java2d;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;

import org.cogaen.core.Core;
import org.cogaen.time.TimeService;
import org.cogaen.time.Timer;

public class AnimationVisual extends Visual {

	public static final String ANIMATION_TIMER = "animation timer";
	public static final double DEFAULT_DISPLAY_TIME = 1.0 / 15.0;
	
	private Image image;
	private AffineTransform transform;
	private int frameWidth;
	private int frameHeight;
	private int frame;
	private int numFrames;
	private int columns;
	private int rows;
	private Timer timer;
	private double elapsedTime;
	private double displayTime;
	private int firstFrame;
	private int lastFrame;
	
	public AnimationVisual(Core core, Image image, int columns, int rows, double width, double height) {
		
		if (columns < 1 || rows <  1) {
			throw new IllegalArgumentException("columns and rows parameter must be greater or equal 1");
		}
		
		int imgWidth = image.getWidth(null);
		int imgHeight = image.getHeight(null);
		this.columns = columns;
		this.rows = rows;
		this.frameWidth = imgWidth / columns;
		this.frameHeight = imgHeight / rows;
		this.numFrames = columns * rows;
		this.firstFrame = 0;
		this.lastFrame = this.numFrames - 1;
		this.frame = firstFrame;
		
		this.transform = AffineTransform.getScaleInstance(width / this.frameWidth, height / this.frameHeight);
		this.transform.translate(-this.frameWidth / 2.0, -this.frameHeight / 2.0);
		this.image = image;
		
		TimeService timeSrv = TimeService.getInstance(core);
		if (timeSrv.hasTimer(ANIMATION_TIMER)) {
			this.timer = timeSrv.getTimer(ANIMATION_TIMER);
		} else {
			this.timer = timeSrv.createTimer(ANIMATION_TIMER);
		}
		this.elapsedTime = 0;
		this.displayTime = DEFAULT_DISPLAY_TIME;		
	}

	public AnimationVisual(AnimationVisual animation) {
		this.image = animation.image;
		this.columns = animation.columns;
		this.rows = animation.rows;
		this.transform = animation.transform;
		
		this.frameWidth = animation.frameWidth;
		this.frameHeight = animation.frameHeight;
		this.frame = animation.frame;
		this.numFrames = animation.numFrames;
		this.timer = animation.timer;
		this.elapsedTime = animation.elapsedTime;
		this.displayTime = animation.displayTime;
		this.firstFrame = animation.firstFrame;
		this.lastFrame = animation.lastFrame;
	}
	
	@Override
	public void render(Graphics2D g) {
		AffineTransform tx = g.getTransform();
		g.transform(this.transform);
		int sx = (this.frame % this.columns) * this.frameWidth;
		int sy = (this.frame / this.columns) * this.frameHeight;
		g.drawImage(this.image, 0, 0, this.frameWidth, this.frameHeight, sx, sy, sx + this.frameWidth, sy + this.frameHeight, null);
		g.setTransform(tx);
		
		this.elapsedTime += this.timer.getDeltaTime();
		if (this.elapsedTime > this.displayTime) {
			this.elapsedTime = 0.0;
			nextFrame();
		}
	}
	
	private void nextFrame() {
		this.frame++;
		
		if (this.frame > this.lastFrame) {
			this.frame = this.firstFrame;
		}
	}

	public int getFrameNumber() {
		return this.frame;
	}
	
	public void setFrameNumber(int frame) {
		this.frame = frame;
	}
	
	public int getNumOfFrames() {
		return this.numFrames;
	}
	
	public void setDisplayTime(double time) {
		this.displayTime = time;
	}
	
	public double getDisplayTime() {
		return this.displayTime;
	}
	
	public void setSequence(int firstFrame, int nFrames) {
		if (firstFrame < 0 || nFrames <= 0 || this.numFrames - firstFrame < nFrames) {
			throw new IllegalArgumentException("invalid animation sequence specified (frame #" 
					+ firstFrame + " to frame #" + (this.firstFrame + nFrames - 1) + ")");
		}
		
		this.firstFrame = firstFrame;
		this.lastFrame = this.firstFrame + nFrames - 1;
		setFrameNumber(this.firstFrame);
	}

}